package com.astro.app;

import java.io.File;
import java.io.FileWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.math.BigDecimal;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.apache.log4j.Logger;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.astro.business.StatementManager;
import com.astro.business.UserManager;
import com.astro.common.Constants;
import com.astro.common.util.ConverterUtil;
import com.astro.common.util.DateUtil;
import com.astro.common.util.PDFGenerator;
import com.astro.domain.bean.PDFData;
import com.astro.domain.bean.Statement;
import com.astro.domain.bean.Transaction;
import com.astro.domain.bean.UserAccount;
import com.checkfree.isolutions.persist.PersistServer;
import com.checkfree.isolutions.statement.FieldValue;
import com.checkfree.isolutions.statement.StatementSection;

public class GenAccPdf extends AppBase {

	static final Logger log = Logger.getLogger(GenAccPdf.class);

	public static void main(String[] args) throws Exception {

		System.out.println(" spring context is loading...");
		AppContext.setSpringContext(new ClassPathXmlApplicationContext(
				"spring-datasource-config.xml"));
		System.out.println(" spring context is loaded!");

		GenAccPdf pdf = new GenAccPdf();
		Date statementDate = DateUtil.dateFormatterOut.parse("01/07/2013");
		Long userId = new Long(2);
		pdf.genPdf(pdf.getPDFData(userId, statementDate));
	}
	
	private PDFData getPDFData(Long userId, Date statementDate) throws Exception{
		boolean txStartedHere = false;
		PDFData pdfData  = null;
		try {
			PersistServer.activate(Constants.MERCHANT);
			txStartedHere = startTransactionIfNeeded();
			UserManager userManager = (UserManager) AppContext.getSpringContext().getBean("userManager");
			List<UserAccount> userAccounts = userManager.listUserAccount(userId);
			pdfData = new PDFData();
			pdfData.setStatementDate(DateUtil.formatLocalSlash(statementDate));
			
			for (UserAccount userAccount : userAccounts) {
				Statement statement = getStatement(userAccount.getAccountNo(), statementDate);
				pdfData.setAmountDue(pdfData.getAmountDue().add(statement.getAcmountDue()));
				pdfData.setPaymentAdjustment(pdfData.getPaymentAdjustment().add(statement.getPaymentAdjustment()));
				pdfData.setPreviousBalance(pdfData.getPreviousBalance().add(statement.getPreviousBalance()));
				pdfData.setTotalAmountDue(pdfData.getTotalAmountDue().add(statement.getTotalAmountDue()));
				pdfData.setUnpaid(pdfData.getUnpaid().add(statement.getUnpaid()));
				
				pdfData.setCustName(statement.getCustName());
				pdfData.setDueDate(statement.getDueDate());
				pdfData.getStatements().add(statement);
			}
		}catch (Exception ex){
			throw ex;
		} finally {
			commitTransactionIfNeeded(txStartedHere);
			PersistServer.deactivate();
			return pdfData;
		}
		
	}

	private Statement getStatement(String accountNo, Date statementDate) throws Exception {
		Statement statement = new Statement();
			StatementManager statementManager = (StatementManager) AppContext.getSpringContext().getBean("statementManager");
			StatementSection rootSection = statementManager.getStatement(accountNo, statementDate).getRootSection();

			if (rootSection != null) {
				statement.setCustName(getFieldText(rootSection,"custname"));
				statement.setAccountNo(getFieldText(rootSection,"fullaccno"));
				statement.setDueDate(getFieldText(rootSection,"duedate"));
				
				StatementSection detailStatement = rootSection.getSection("DetailsSection");
				StatementSection summaryOfStatement = detailStatement.getSection("SummaryOfChargesSection");

				statement.setAcmountDue(ConverterUtil.ToBigDecimal(getFieldText(rootSection, "dueamt")));
				statement.setPreviousBalance(ConverterUtil.ToBigDecimal(getFieldText(summaryOfStatement,"PreviousMonthBalance")));
				statement.setPaymentAdjustment(ConverterUtil.ToBigDecimal(getFieldText(summaryOfStatement,"PayOrAdjustments")));
				statement.setUnpaid(ConverterUtil.ToBigDecimal(getFieldText(summaryOfStatement, "Unpaid")));
				statement.setNewCharge(ConverterUtil.ToBigDecimal(getFieldText(summaryOfStatement, "NewCharges")));
				statement.setTotalAmountDue(ConverterUtil.ToBigDecimal(getFieldText(summaryOfStatement, "TotalAmountDue")));
				
				List<Transaction> transactions = new ArrayList<Transaction>();
				statement.setTransactions(transactions);
				if (detailStatement != null) {
					int chargesRepeatSectionCount = (int) detailStatement.getSectionCount("ChargesRepeatSection");
					for (int i = 0; i < chargesRepeatSectionCount; i++) {
						StatementSection chargesRepeatSection = detailStatement.getRepeatSection("ChargesRepeatSection", i);
						Transaction trans = new Transaction();
						trans.setItem(getField(chargesRepeatSection, "Item"));
						trans.setSmartCardNo(getField(chargesRepeatSection,"SmartCardNum"));
						trans.setAmountCharge(ConverterUtil.ToBigDecimal(getField(chargesRepeatSection,"AmountCharged")));
						trans.setProrationPeriodFrom(getField(chargesRepeatSection,"FromDt"));
						trans.setProrationPeriodTo(getField(chargesRepeatSection, "ToDt"));
						trans.setGst(trans.getAmountCharge().multiply(new BigDecimal(0.06)).setScale(2, BigDecimal.ROUND_UP));
						trans.setTotal(trans.getAmountCharge().add(trans.getGst()));
						transactions.add(trans);
					}
				}
			}
		return statement;
		
	}

	/**
	 * Generate PDF report
	 */
	public byte[] genPdf(PDFData pdfData) throws Exception {
		byte[] encoded = null;
		try {
			AppConfig appConfig = (AppConfig) AppContext.getSpringContext()
					.getBean(AppConfig.class);
			// For storing content in html file
			String time = new Date().getTime() + "";
			String contentPath = appConfig.getTempPath() + time + ".html";
			String outputPath = appConfig.getTempPath() + time + ".pdf";
			Velocity.setProperty("resource.loader", "class");
			Velocity.setProperty("class.resource.loader.class",
					"org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
			Velocity.init();
			Template t = Velocity.getTemplate("template/report-template.html");
			VelocityContext ctx = new VelocityContext();

			ctx.put("pdfData", pdfData);

			Writer writer = new StringWriter();
			t.merge(ctx, writer);

			FileWriter fw = new FileWriter(contentPath);
			fw.write(writer.toString());
			fw.flush();
			fw.close();

			PDFGenerator.generatePdfFile(contentPath, outputPath);
			
			encoded = Files.readAllBytes(Paths.get(outputPath));
			
			// Delete temporary files
			try {
				//Files.deleteIfExists(Paths.get(outputPath));
				Files.deleteIfExists(Paths.get(contentPath));
			} catch (Exception e) {
				log.error(e.getMessage());
			}

		} catch (Exception e) {
			log.error(e.getMessage(), e);
		} catch (Throwable e) {
			log.error(e.getMessage(), e);
		}
		
		return encoded;
	}

	private String getFieldText(StatementSection statementSection, String name) {
		return statementSection.getFieldText(name);
	}
	
	private String getField(StatementSection statementSection, String name) {
		FieldValue fieldValue = statementSection.getField(name);
		if (fieldValue == null) return "";
		else return fieldValue.asString();
	}
}
